View Javadoc

1   // RuntimeCodeAllocator.java, created Tue Feb 27  2:53:11 2001 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   package joeq.Allocator;
5   
6   import java.util.List;
7   import joeq.Class.jq_BytecodeMap;
8   import joeq.Class.jq_CompiledCode;
9   import joeq.Class.jq_Method;
10  import joeq.Class.jq_TryCatch;
11  import joeq.Memory.Address;
12  import joeq.Memory.CodeAddress;
13  import joeq.Runtime.ExceptionDeliverer;
14  import joeq.Runtime.SystemInterface;
15  import jwutil.strings.Strings;
16  import jwutil.util.Assert;
17  
18  /***
19   * RuntimeCodeAllocator
20   *
21   * @author  John Whaley <jwhaley@alum.mit.edu>
22   * @version $Id: RuntimeCodeAllocator.java 1941 2004-09-30 03:37:06Z joewhaley $
23   */
24  public class RuntimeCodeAllocator extends CodeAllocator {
25  
26      // Memory layout:
27      //
28      // start:   |   next ptr   |---->
29      //          |   end ptr    |
30      //          |.....data.....|
31      // current: |.....free.....|
32      // end:     | current ptr  |
33  
34      /*** Size of blocks allocated from the OS.
35       */
36      public static final int BLOCK_SIZE = 131072;
37      
38      /*** Pointers to the start, current, and end of the heap.
39       */
40      private CodeAddress heapStart, heapCurrent, heapEnd;
41      
42      /*** Pointer to the first block.
43       */
44      private CodeAddress heapFirst;
45      
46      /*** Max memory free in all allocated blocks.
47       */
48      private int maxFreePrevious;
49      
50      volatile boolean isGenerating = false;
51      
52      public void init()
53      throws OutOfMemoryError {
54          heapStart = heapFirst = (CodeAddress)SystemInterface.syscalloc(BLOCK_SIZE);
55          if (heapStart.isNull())
56              HeapAllocator.outOfMemory();
57          heapStart.poke(null);
58          heapEnd = (CodeAddress)heapStart.offset(BLOCK_SIZE - CodeAddress.size());
59          heapStart.offset(CodeAddress.size()).poke(heapEnd);
60          heapCurrent = (CodeAddress)heapStart.offset(CodeAddress.size() * 2);
61          heapEnd.poke(heapCurrent);
62      }
63      
64      /*** Allocate a code buffer of the given estimated size, such that the given
65       * offset will have the given alignment.
66       * It is legal for code to exceed the estimated size, but the cost may be
67       * high (i.e. it may require recopying of the buffer.)
68       *
69       * @param estimatedSize  estimated size, in bytes, of desired code buffer
70       * @param offset  desired offset to align to
71       * @param alignment  desired alignment, or 0 if don't care
72       * @return  the new code buffer
73       */
74      public x86CodeBuffer getCodeBuffer(int estimatedSize,
75                                         int offset,
76                                         int alignment) {
77          // should not be called recursively.
78          Assert._assert(!isGenerating);
79          if (TRACE) SystemInterface.debugwriteln("Code generation started: "+this);
80          isGenerating = true;
81          // align pointer
82          CodeAddress entrypoint = (CodeAddress)heapCurrent.offset(offset);
83          if (alignment > 0) entrypoint.align(alignment);
84          if (entrypoint.offset(estimatedSize - offset).difference(heapEnd) <= 0) {
85              return new Runtimex86CodeBuffer((CodeAddress)entrypoint.offset(-offset), heapEnd);
86          }
87          if (estimatedSize < maxFreePrevious) {
88              // use a prior block's unused space.
89              if (TRACE) SystemInterface.debugwriteln("Estimated size ("+Strings.hex(estimatedSize)+" fits within a prior block: maxfreeprev="+Strings.hex(maxFreePrevious));
90              // start searching at the first block
91              CodeAddress start_ptr = heapFirst;
92              for (;;) {
93                  // start_ptr:   points to start of current block
94                  // end_ptr:     points to end of current block
95                  // current_ptr: current pointer for current block
96                  Assert._assert(!start_ptr.isNull());
97                  CodeAddress end_ptr = (CodeAddress)start_ptr.offset(CodeAddress.size()).peek();
98                  CodeAddress current_ptr = (CodeAddress)end_ptr.peek();
99                  if (end_ptr.difference(current_ptr) >= estimatedSize) {
100                     return new Runtimex86CodeBuffer(current_ptr, end_ptr);
101                 }
102                 start_ptr = (CodeAddress)start_ptr.peek(); // go to the next block
103             }
104         }
105         // allocate new block.
106         allocateNewBlock(Math.max(estimatedSize, BLOCK_SIZE));
107         return new Runtimex86CodeBuffer(heapCurrent, heapEnd);
108     }
109     
110     private void allocateNewBlock(int blockSize)
111     throws OutOfMemoryError {
112         heapStart.offset(CodeAddress.size()).poke(heapCurrent);
113         CodeAddress newBlock = (CodeAddress)SystemInterface.syscalloc(blockSize);
114         if (newBlock.isNull())
115             HeapAllocator.outOfMemory();
116         heapStart.poke(newBlock);
117         heapStart = newBlock;
118         heapStart.poke(null);
119         heapEnd = (CodeAddress)newBlock.offset(blockSize - CodeAddress.size());
120         heapStart.offset(CodeAddress.size()).poke(heapEnd);
121         heapCurrent = (CodeAddress)newBlock.offset(CodeAddress.size() * 2);
122         heapEnd.poke(heapCurrent);
123     }
124     
125     public void patchAbsolute(Address addr1, Address addr2) {
126         addr1.poke(addr2);
127     }
128     public void patchRelativeOffset(CodeAddress code, CodeAddress target) {
129         code.poke4(target.difference(code)-4);
130     }
131     
132     public class Runtimex86CodeBuffer extends CodeAllocator.x86CodeBuffer {
133 
134         private CodeAddress startAddress;
135         private CodeAddress entrypointAddress;
136         private CodeAddress currentAddress;
137         private CodeAddress endAddress;
138 
139         Runtimex86CodeBuffer(CodeAddress startAddress, CodeAddress endAddress) {
140             this.startAddress = startAddress;
141             this.endAddress = endAddress;
142             this.currentAddress = (CodeAddress)startAddress.offset(-1);
143         }
144         
145         public int getCurrentOffset() { return currentAddress.difference(startAddress) + 1; }
146         public CodeAddress getStartAddress() { return startAddress; }
147         public CodeAddress getCurrentAddress() { return (CodeAddress)currentAddress.offset(1); }
148         
149         public CodeAddress getStart() { return startAddress; }
150         public CodeAddress getCurrent() { return (CodeAddress)currentAddress.offset(1); }
151         public CodeAddress getEntry() { return entrypointAddress; }
152         public CodeAddress getEnd() { return endAddress; }
153         
154         public void setEntrypoint() { this.entrypointAddress = getCurrent(); }
155         
156         public void checkSize(int size) {
157             if (currentAddress.offset(size).difference(endAddress) < 0) return;
158             // overflow!
159             int newEstimatedSize = endAddress.difference(startAddress) << 1;
160             allocateNewBlock(Math.max(BLOCK_SIZE, newEstimatedSize));
161             Assert._assert(currentAddress.difference(startAddress)+size < heapEnd.difference(heapCurrent));
162             SystemInterface.mem_cpy(heapCurrent, startAddress, currentAddress.difference(startAddress));
163             if (!entrypointAddress.isNull())
164                 entrypointAddress = (CodeAddress)heapCurrent.offset(entrypointAddress.difference(startAddress));
165             currentAddress = (CodeAddress)heapCurrent.offset(currentAddress.difference(startAddress));
166             startAddress = heapCurrent;
167             endAddress = heapEnd;
168         }
169         
170         public void add1(byte i) {
171             checkSize(1);
172             currentAddress = (CodeAddress)currentAddress.offset(1);
173             currentAddress.poke1(i);
174         }
175         public void add2_endian(int i) {
176             checkSize(2);
177             currentAddress.offset(1).poke2((short)i);
178             currentAddress = (CodeAddress)currentAddress.offset(2);
179         }
180         public void add2(int i) {
181             checkSize(2);
182             currentAddress.offset(1).poke2(endian2(i));
183             currentAddress = (CodeAddress)currentAddress.offset(2);
184         }
185         public void add3(int i) {
186             checkSize(3);
187             currentAddress.offset(1).poke1((byte)(i >> 16));
188             currentAddress.offset(2).poke2(endian2(i));
189             currentAddress = (CodeAddress)currentAddress.offset(3);
190         }
191         public void add4_endian(int i) {
192             checkSize(4);
193             currentAddress.offset(1).poke4(i);
194             currentAddress = (CodeAddress)currentAddress.offset(4);
195         }
196 
197         public byte get1(int k) {
198             return startAddress.offset(k).peek1();
199         }
200         public int get4_endian(int k) {
201             return startAddress.offset(k).peek4();
202         }
203 
204         public void put1(int k, byte instr) {
205             startAddress.offset(k).poke1(instr);
206         }
207         public void put4_endian(int k, int instr) {
208             startAddress.offset(k).poke4(instr);
209         }
210 
211         public void skip(int nbytes) {
212             currentAddress = (CodeAddress)currentAddress.offset(nbytes);
213         }
214         
215         public jq_CompiledCode allocateCodeBlock(jq_Method m, jq_TryCatch[] ex,
216                                                  jq_BytecodeMap bcm, ExceptionDeliverer exd,
217                                                  int stackframesize,
218                                                  List code_relocs, List data_relocs) {
219             Assert._assert(isGenerating);
220             CodeAddress start = getStart();
221             CodeAddress entrypoint = getEntry();
222             CodeAddress current = getCurrent();
223             CodeAddress end = getEnd();
224             Assert._assert(current.difference(end) <= 0);
225             if (end != heapEnd) {
226                 if (TRACE) SystemInterface.debugwriteln("Prior block, recalculating maxfreeprevious (was "+Strings.hex(maxFreePrevious)+")");
227                 // prior block
228                 end.poke(current);
229                 // recalculate max free previous
230                 maxFreePrevious = 0;
231                 CodeAddress start_ptr = heapFirst;
232                 while (!start_ptr.isNull()) {
233                     CodeAddress end_ptr = (CodeAddress)start_ptr.offset(CodeAddress.size());
234                     CodeAddress current_ptr = (CodeAddress)end_ptr.peek();
235                     int temp = end_ptr.difference(current_ptr);
236                     maxFreePrevious = Math.max(maxFreePrevious, temp);
237                     start_ptr = (CodeAddress)start_ptr.peek();
238                 }
239                 if (TRACE) SystemInterface.debugwriteln("New maxfreeprevious: "+Strings.hex(maxFreePrevious));
240             } else {
241                 // current block
242                 heapCurrent = current;
243                 heapEnd.poke(heapCurrent);
244             }
245             isGenerating = false;
246             if (TRACE) SystemInterface.debugwriteln("Code generation completed: "+this);
247             jq_CompiledCode cc = new jq_CompiledCode(m, start, current.difference(start), entrypoint, ex, bcm, exd, stackframesize, code_relocs, data_relocs);
248             CodeAllocator.registerCode(cc);
249             return cc;
250         }
251     }
252 
253     public static short endian2(int k) {
254         return (short)(((k>>8)&0xFF) | (k<<8));
255     }
256 }